सस्पेंस का उपयोग करके React में समानांतर डेटा प्राप्त करने के लिए उन्नत तकनीकों का अन्वेषण करें, जिससे एप्लिकेशन प्रदर्शन और उपयोगकर्ता अनुभव बेहतर होता है। कई एसिंक्रोनस कार्यों का समन्वय करने और प्रभावी रूप से लोडिंग राज्यों को संभालने के लिए रणनीतियों का अन्वेषण करें।
React Suspense समन्वय: समानांतर डेटा प्राप्त करने की रणनीतियों में महारत हासिल करना
React Suspense ने हमारे एसिंक्रोनस कार्यों, विशेष रूप से डेटा प्राप्त करने को संभालने के तरीके में क्रांति ला दी है। यह घटकों को डेटा लोड होने की प्रतीक्षा करते हुए रेंडरिंग को "सस्पेंड" करने की अनुमति देता है, जिससे लोडिंग स्टेट्स को प्रबंधित करने का एक घोषणात्मक तरीका मिलता है। हालाँकि, केवल Suspense के साथ अलग-अलग डेटा प्राप्त करने को रैप करने से एक झरना प्रभाव हो सकता है, जहाँ एक फ़ेच अगले के शुरू होने से पहले पूरा हो जाता है, जिससे प्रदर्शन पर नकारात्मक प्रभाव पड़ता है। यह ब्लॉग पोस्ट Suspense का उपयोग करके समानांतर में कई डेटा फ़ेच को समन्वयित करने, आपके एप्लिकेशन की प्रतिक्रियाशीलता को अनुकूलित करने और वैश्विक दर्शकों के लिए उपयोगकर्ता अनुभव को बढ़ाने के लिए उन्नत रणनीतियों पर प्रकाश डालता है।
डेटा प्राप्त करने में झरना समस्या को समझना
एक ऐसे परिदृश्य की कल्पना करें जहाँ आपको उपयोगकर्ता प्रोफ़ाइल को उनके नाम, अवतार और हाल की गतिविधि के साथ प्रदर्शित करने की आवश्यकता है। यदि आप प्रत्येक डेटा को क्रमिक रूप से प्राप्त करते हैं, तो उपयोगकर्ता नाम के लिए लोडिंग स्पिनर देखता है, फिर अवतार के लिए एक और, और अंत में, गतिविधि फ़ीड के लिए एक। यह क्रमिक लोडिंग पैटर्न एक झरना प्रभाव पैदा करता है, जो पूरी प्रोफ़ाइल के रेंडरिंग में देरी करता है और उपयोगकर्ताओं को निराश करता है। अलग-अलग नेटवर्क गति वाले अंतर्राष्ट्रीय उपयोगकर्ताओं के लिए, यह देरी और भी अधिक स्पष्ट हो सकती है।
इस सरलीकृत कोड स्निपेट पर विचार करें:
function UserProfile() {
const name = useName(); // उपयोगकर्ता नाम प्राप्त करता है
const avatar = useAvatar(name); // नाम के आधार पर अवतार प्राप्त करता है
const activity = useActivity(name); // नाम के आधार पर गतिविधि प्राप्त करता है
return (
<div>
<h2>{name}</h2>
<img src={avatar} alt="User Avatar" />
<ul>
{activity.map(item => <li key={item.id}>{item.text}</li>)}
</ul>
</div>
);
}
इस उदाहरण में, useAvatar और useActivity useName के परिणाम पर निर्भर हैं। यह एक स्पष्ट झरना बनाता है - useAvatar और useActivity तब तक डेटा प्राप्त करना शुरू नहीं कर सकते जब तक कि useName पूरा नहीं हो जाता। यह अक्षम है और एक सामान्य प्रदर्शन बाधा है।
Suspense के साथ समानांतर डेटा प्राप्त करने की रणनीतियाँ
Suspense के साथ डेटा प्राप्त करने को अनुकूलित करने की कुंजी सभी डेटा अनुरोधों को एक साथ शुरू करना है। यहाँ कुछ रणनीतियाँ दी गई हैं जिन्हें आप नियोजित कर सकते हैं:
1. `React.preload` और संसाधनों के साथ डेटा प्रीलोड करना
सबसे शक्तिशाली तकनीकों में से एक घटक के रेंडर होने से पहले ही डेटा को प्रीलोड करना है। इसमें एक "संसाधन" (एक ऑब्जेक्ट जो डेटा प्राप्त करने के वादे को एनकैप्सुलेट करता है) बनाना और डेटा को प्री-फ़ेच करना शामिल है। `React.preload` इसमें मदद करता है। जब तक घटक को डेटा की आवश्यकता होती है, तब तक यह पहले से ही उपलब्ध होता है, जिससे लोडिंग स्टेट लगभग पूरी तरह से समाप्त हो जाता है।
उत्पाद प्राप्त करने के लिए एक संसाधन पर विचार करें:
const createProductResource = (productId) => {
let promise;
let product;
let error;
const suspender = new Promise((resolve, reject) => {
promise = fetch(`/api/products/${productId}`)
.then(response => {
if (!response.ok) {
throw new Error(`HTTP error! Status: ${response.status}`);
}
return response.json();
})
.then(data => {
product = data;
resolve();
})
.catch(e => {
error = e;
reject(e);
});
});
return {
read() {
if (error) {
throw error;
}
if (product) {
return product;
}
throw suspender;
},
};
};
// उपयोग:
const productResource = createProductResource(123);
function ProductDetails() {
const product = productResource.read();
return (<div>{product.name}</div>);
}
अब, आप ProductDetails घटक के रेंडर होने से पहले इस संसाधन को प्रीलोड कर सकते हैं। उदाहरण के लिए, रूट संक्रमण के दौरान या होवर पर।
React.preload(productResource);
यह सुनिश्चित करता है कि ProductDetails घटक को इसकी आवश्यकता होने तक डेटा उपलब्ध होने की संभावना है, जिससे लोडिंग स्टेट कम या समाप्त हो जाता है।
2. समवर्ती डेटा प्राप्त करने के लिए `Promise.all` का उपयोग करना
एक और सरल और प्रभावी तरीका एक ही Suspense सीमा के भीतर सभी डेटा फ़ेच को एक साथ शुरू करने के लिए Promise.all का उपयोग करना है। यह तब अच्छी तरह से काम करता है जब डेटा निर्भरताएँ पहले से ज्ञात हों।
आइए उपयोगकर्ता प्रोफ़ाइल उदाहरण पर दोबारा गौर करें। डेटा को क्रमिक रूप से प्राप्त करने के बजाय, हम नाम, अवतार और गतिविधि फ़ीड को एक साथ प्राप्त कर सकते हैं:
import { useState, useEffect, Suspense } from 'react';
async function fetchName() {
// API कॉल का अनुकरण करें
await new Promise(resolve => setTimeout(resolve, 500));
return 'जॉन डो';
}
async function fetchAvatar(name) {
// API कॉल का अनुकरण करें
await new Promise(resolve => setTimeout(resolve, 300));
return `https://example.com/avatars/${name.toLowerCase().replace(' ', '-')}.jpg`;
}
async function fetchActivity(name) {
// API कॉल का अनुकरण करें
await new Promise(resolve => setTimeout(resolve, 800));
return [
{ id: 1, text: 'एक तस्वीर पोस्ट की' },
{ id: 2, text: 'प्रोफ़ाइल अपडेट की' },
];
}
function useSuspense(promise) {
const [result, setResult] = useState(null);
useEffect(() => {
let didCancel = false;
promise.then(
(data) => {
if (!didCancel) {
setResult({ status: 'success', value: data });
}
},
(error) => {
if (!didCancel) {
setResult({ status: 'error', value: error });
}
}
);
return () => {
didCancel = true;
};
}, [promise]);
if (result?.status === 'success') {
return result.value;
} else if (result?.status === 'error') {
throw result.value;
} else {
throw promise;
}
}
function Name() {
const name = useSuspense(fetchName());
return <h2>{name}</h2>;
}
function Avatar({ name }) {
const avatar = useSuspense(fetchAvatar(name));
return <img src={avatar} alt="User Avatar" />;
}
function Activity({ name }) {
const activity = useSuspense(fetchActivity(name));
return (
<ul>
{activity.map(item => <li key={item.id}>{item.text}</li>)}
</ul>
);
}
function UserProfile() {
const name = useSuspense(fetchName());
return (
<div>
<Suspense fallback=<div>अवतार लोड हो रहा है...</div>>
<Avatar name={name} />
</Suspense>
<Suspense fallback=<div>गतिविधि लोड हो रही है...</div>>
<Activity name={name} />
</Suspense>
</div>
);
}
export default UserProfile;
हालांकि, यदि प्रत्येक `Avatar` और `Activity` भी `fetchName` पर निर्भर करते हैं, लेकिन अलग-अलग सस्पेंस सीमाओं के अंदर रेंडर किए जाते हैं, तो आप `fetchName` वादे को माता-पिता तक ले जा सकते हैं और इसे React Context के माध्यम से प्रदान कर सकते हैं।
import React, { createContext, useContext, useState, useEffect, Suspense } from 'react';
async function fetchName() {
// API कॉल का अनुकरण करें
await new Promise(resolve => setTimeout(resolve, 500));
return 'जॉन डो';
}
async function fetchAvatar(name) {
// API कॉल का अनुकरण करें
await new Promise(resolve => setTimeout(resolve, 300));
return `https://example.com/avatars/${name.toLowerCase().replace(' ', '-')}.jpg`;
}
async function fetchActivity(name) {
// API कॉल का अनुकरण करें
await new Promise(resolve => setTimeout(resolve, 800));
return [
{ id: 1, text: 'एक तस्वीर पोस्ट की' },
{ id: 2, text: 'प्रोफ़ाइल अपडेट की' },
];
}
function useSuspense(promise) {
const [result, setResult] = useState(null);
useEffect(() => {
let didCancel = false;
promise.then(
(data) => {
if (!didCancel) {
setResult({ status: 'success', value: data });
}
},
(error) => {
if (!didCancel) {
setResult({ status: 'error', value: error });
}
}
);
return () => {
didCancel = true;
};
}, [promise]);
if (result?.status === 'success') {
return result.value;
} else if (result?.status === 'error') {
throw result.value;
} else {
throw promise;
}
}
const NamePromiseContext = createContext(null);
function Avatar() {
const namePromise = useContext(NamePromiseContext);
const name = useSuspense(namePromise);
const avatar = useSuspense(fetchAvatar(name));
return <img src={avatar} alt="User Avatar" />;
}
function Activity() {
const namePromise = useContext(NamePromiseContext);
const name = useSuspense(namePromise);
const activity = useSuspense(fetchActivity(name));
return (
<ul>
{activity.map(item => <li key={item.id}>{item.text}</li>)}
</ul>
);
}
function UserProfile() {
const namePromise = fetchName();
return (
<NamePromiseContext.Provider value={namePromise}>
<Suspense fallback=<div>अवतार लोड हो रहा है...</div>>
<Avatar />
</Suspense>
<Suspense fallback=<div>गतिविधि लोड हो रही है...</div>>
<Activity />
</Suspense>
</NamePromiseContext.Provider>
);
}
export default UserProfile;
3. समानांतर फ़ेच को प्रबंधित करने के लिए एक कस्टम हुक का उपयोग करना
संभावित रूप से सशर्त डेटा निर्भरताओं वाले अधिक जटिल परिदृश्यों के लिए, आप समानांतर डेटा प्राप्त करने को प्रबंधित करने के लिए एक कस्टम हुक बना सकते हैं और एक संसाधन लौटा सकते हैं जिसका उपयोग Suspense कर सकता है।
import { useState, useEffect, useRef } from 'react';
function useParallelData(fetchFunctions) {
const [resource, setResource] = useState(null);
const mounted = useRef(true);
useEffect(() => {
mounted.current = true;
const promises = fetchFunctions.map(fn => fn());
const suspender = Promise.all(promises).then(
(results) => {
if (mounted.current) {
setResource({ status: 'success', value: results });
}
},
(error) => {
if (mounted.current) {
setResource({ status: 'error', value: error });
}
}
);
setResource({
status: 'pending',
value: suspender,
});
return () => {
mounted.current = false;
};
}, [fetchFunctions]);
const read = () => {
if (!resource) {
throw new Error('संसाधन अभी तक प्रारंभ नहीं किया गया है');
}
if (resource.status === 'pending') {
throw resource.value;
}
if (resource.status === 'error') {
throw resource.value;
}
return resource.value;
};
return { read };
}
// उदाहरण उपयोग:
async function fetchUserData(userId) {
// API कॉल का अनुकरण करें
await new Promise(resolve => setTimeout(resolve, 300));
return { id: userId, name: 'उपयोगकर्ता ' + userId };
}
async function fetchUserPosts(userId) {
// API कॉल का अनुकरण करें
await new Promise(resolve => setTimeout(resolve, 500));
return [{ id: 1, title: 'पोस्ट 1' }, { id: 2, title: 'पोस्ट 2' }];
}
function UserProfile({ userId }) {
const { read } = useParallelData([
() => fetchUserData(userId),
() => fetchUserPosts(userId),
]);
const [userData, userPosts] = read();
return (
<div>
<h2>{userData.name}</h2>
<ul>
{userPosts.map(post => <li key={post.id}>{post.title}</li>)}
</ul>
</div>
);
}
function App() {
return (
<Suspense fallback=<div>उपयोगकर्ता डेटा लोड हो रहा है...</div>>
<UserProfile userId={123} />
</Suspense>
);
}
export default App;
यह दृष्टिकोण हुक के भीतर वादों और लोडिंग स्टेट्स को प्रबंधित करने की जटिलता को समाहित करता है, जिससे घटक कोड क्लीनर और डेटा को रेंडर करने पर अधिक केंद्रित हो जाता है।
4. स्ट्रीमिंग सर्वर रेंडरिंग के साथ चुनिंदा हाइड्रेशन
सर्वर-रेंडर किए गए अनुप्रयोगों के लिए, React 18 स्ट्रीमिंग सर्वर रेंडरिंग के साथ चुनिंदा हाइड्रेशन का परिचय देता है। यह आपको सर्वर पर उपलब्ध होने पर HTML को क्लाइंट को चंक में भेजने की अनुमति देता है। आप धीमी गति से लोड होने वाले घटकों को <Suspense> सीमाओं के साथ रैप कर सकते हैं, जिससे बाकी पेज को इंटरैक्टिव बनने की अनुमति मिलती है, जबकि धीमे घटक अभी भी सर्वर पर लोड हो रहे हैं। यह विशेष रूप से धीमे नेटवर्क कनेक्शन या डिवाइस वाले उपयोगकर्ताओं के लिए कथित प्रदर्शन में नाटकीय रूप से सुधार करता है।
एक ऐसे परिदृश्य पर विचार करें जहाँ एक समाचार वेबसाइट को दुनिया के विभिन्न क्षेत्रों (जैसे, एशिया, यूरोप, अमेरिका) से लेख प्रदर्शित करने की आवश्यकता है। कुछ डेटा स्रोत दूसरों की तुलना में धीमे हो सकते हैं। चुनिंदा हाइड्रेशन धीमी गति से लोड होने वाले क्षेत्रों के लेखों को अभी भी लोड होने के दौरान तेज़ क्षेत्रों से लेखों को प्रदर्शित करने की अनुमति देता है, जिससे पूरा पेज ब्लॉक होने से बच जाता है।
त्रुटियों और लोडिंग स्टेट्स को संभालना
जबकि Suspense लोडिंग स्टेट प्रबंधन को सरल करता है, त्रुटि हैंडलिंग महत्वपूर्ण बनी हुई है। त्रुटि सीमाएँ (componentDidCatch जीवनचक्र विधि या `react-error-boundary` जैसे पुस्तकालयों से useErrorBoundary हुक का उपयोग करके) आपको डेटा प्राप्त करने या रेंडरिंग के दौरान होने वाली त्रुटियों को कुशलता से संभालने की अनुमति देती हैं। इन त्रुटि सीमाओं को विशिष्ट Suspense सीमाओं के भीतर त्रुटियों को पकड़ने के लिए रणनीतिक रूप से रखा जाना चाहिए, जिससे पूरे एप्लिकेशन को क्रैश होने से रोका जा सके।
import React, { Suspense } from 'react';
import { ErrorBoundary } from 'react-error-boundary';
function MyComponent() {
// ... डेटा प्राप्त करता है जो त्रुटि कर सकता है
}
function App() {
return (
<ErrorBoundary fallback={<div>कुछ गलत हो गया!</div>}>
<Suspense fallback={<div>लोड हो रहा है...</div>}>
<MyComponent />
</Suspense>
</ErrorBoundary>
);
}
लोडिंग और त्रुटि दोनों राज्यों के लिए जानकारीपूर्ण और उपयोगकर्ता-अनुकूल फ़ॉलबैक UI प्रदान करना याद रखें। यह विशेष रूप से अंतर्राष्ट्रीय उपयोगकर्ताओं के लिए महत्वपूर्ण है जो धीमी नेटवर्क गति या क्षेत्रीय सेवा आउटेज का सामना कर सकते हैं।
Suspense के साथ डेटा प्राप्त करने को अनुकूलित करने के लिए सर्वोत्तम अभ्यास
- महत्वपूर्ण डेटा को पहचानें और प्राथमिकता दें: निर्धारित करें कि आपके एप्लिकेशन के प्रारंभिक रेंडरिंग के लिए कौन सा डेटा आवश्यक है और उस डेटा को पहले प्राप्त करने को प्राथमिकता दें।
- जब संभव हो तो डेटा प्रीलोड करें: घटकों को इसकी आवश्यकता होने से पहले डेटा को प्रीलोड करने के लिए `React.preload` और संसाधनों का उपयोग करें, जिससे लोडिंग स्टेट्स कम हो जाएं।
- डेटा को एक साथ प्राप्त करें: समानांतर में कई डेटा फ़ेच शुरू करने के लिए `Promise.all` या कस्टम हुक का उपयोग करें।
- API एंडपॉइंट्स को ऑप्टिमाइज़ करें: सुनिश्चित करें कि आपके API एंडपॉइंट्स प्रदर्शन के लिए ऑप्टिमाइज़ किए गए हैं, जिससे विलंबता और पेलोड आकार कम हो। केवल अपनी आवश्यकतानुसार डेटा प्राप्त करने के लिए GraphQL जैसी तकनीकों का उपयोग करने पर विचार करें।
- कैशिंग लागू करें: API अनुरोधों की संख्या को कम करने के लिए अक्सर एक्सेस किए जाने वाले डेटा को कैश करें। मजबूत कैशिंग क्षमताओं के लिए `swr` या `react-query` जैसी लाइब्रेरी का उपयोग करने पर विचार करें।
- कोड स्प्लिटिंग का उपयोग करें: प्रारंभिक लोड समय को कम करने के लिए अपने एप्लिकेशन को छोटे चंक में विभाजित करें। अपने एप्लिकेशन के विभिन्न हिस्सों को उत्तरोत्तर लोड और रेंडर करने के लिए Suspense के साथ कोड स्प्लिटिंग को मिलाएं।
- प्रदर्शन की निगरानी करें: प्रदर्शन बाधाओं की पहचान करने और उन्हें दूर करने के लिए लाइटहाउस या वेबपेजटेस्ट जैसे उपकरणों का उपयोग करके नियमित रूप से अपने एप्लिकेशन के प्रदर्शन की निगरानी करें।
- त्रुटियों को कुशलता से संभालें: डेटा प्राप्त करने और रेंडरिंग के दौरान त्रुटियों को पकड़ने के लिए त्रुटि सीमाओं को लागू करें, जिससे उपयोगकर्ताओं को जानकारीपूर्ण त्रुटि संदेश प्रदान किए जा सकें।
- सर्वर-साइड रेंडरिंग (SSR) पर विचार करें: SEO और प्रदर्शन कारणों से, तेज़ प्रारंभिक अनुभव प्रदान करने के लिए स्ट्रीमिंग और चुनिंदा हाइड्रेशन के साथ SSR का उपयोग करने पर विचार करें।
निष्कर्ष
React Suspense, जब समानांतर डेटा प्राप्त करने की रणनीतियों के साथ जोड़ा जाता है, तो प्रतिक्रियाशील और प्रदर्शन करने वाले वेब एप्लिकेशन बनाने के लिए एक शक्तिशाली टूलकिट प्रदान करता है। झरना समस्या को समझने और प्रीलोडिंग, Promise.all के साथ समवर्ती फ़ेचिंग और कस्टम हुक जैसी तकनीकों को लागू करने से, आप उपयोगकर्ता अनुभव को महत्वपूर्ण रूप से बेहतर बना सकते हैं। त्रुटियों को कुशलता से संभालना और यह सुनिश्चित करने के लिए प्रदर्शन की निगरानी करना याद रखें कि आपका एप्लिकेशन दुनिया भर के उपयोगकर्ताओं के लिए अनुकूलित रहे। जैसे-जैसे React का विकास जारी है, स्ट्रीमिंग सर्वर रेंडरिंग के साथ चुनिंदा हाइड्रेशन जैसी नई सुविधाओं की खोज से आपकी स्थान या नेटवर्क स्थितियों की परवाह किए बिना, असाधारण उपयोगकर्ता अनुभव प्रदान करने की क्षमता और बढ़ जाएगी। इन तकनीकों को अपनाकर, आप ऐसे एप्लिकेशन बना सकते हैं जो न केवल कार्यात्मक हैं बल्कि आपके वैश्विक दर्शकों के लिए उपयोग करने में भी आनंददायक हैं।
इस ब्लॉग पोस्ट का उद्देश्य React Suspense के साथ समानांतर डेटा प्राप्त करने की रणनीतियों का एक व्यापक अवलोकन प्रदान करना है। हमें उम्मीद है कि आपको यह जानकारीपूर्ण और सहायक लगा होगा। हम आपको अपनी परियोजनाओं में इन तकनीकों के साथ प्रयोग करने और समुदाय के साथ अपनी खोजों को साझा करने के लिए प्रोत्साहित करते हैं।